// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件包含节点上的转换函数，即类型检查包执行的与
// 转换。这些转换函数是
// 类型检查功能不同的
// 原始类型检查函数的精简副本，删除与以下内容相关的所有代码：
// 
// -检测编译时错误（已由types2完成）
// -设置现有节点的实际类型（已根据
// types2中的类型信息完成）
// -处理非类型常量（types2已解决）
// 
// 和typecheck标志。如果转换函数替换或添加新的
// 节点，它将为这些新节点设置类型和类型检查标志。

package noder

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"fmt"
	"go/constant"
)

// 表达式的转换函数

// transformAdd转换一个加法操作（目前只是对
// 字符串的加法）。对应于typecheck中的“二进制运算符”情况。打字检查1。
func transformAdd(n *ir.BinaryExpr) ir.Node {
	assert(n.Type() != nil && n.Typecheck() == 1)
	l := n.X
	if l.Type().IsString() {
		var add *ir.AddStringExpr
		if l.Op() == ir.OADDSTR {
			add = l.(*ir.AddStringExpr)
			add.SetPos(n.Pos())
		} else {
			add = ir.NewAddStringExpr(n.Pos(), []ir.Node{l})
		}
		r := n.Y
		if r.Op() == ir.OADDSTR {
			r := r.(*ir.AddStringExpr)
			add.List.Append(r.List.Take()...)
		} else {
			add.List.Append(r)
		}
		typed(l.Type(), add)
		return add
	}
	return n
}

// 对应于类型检查。Stringtorunesit。
func stringtoruneslit(n *ir.ConvExpr) ir.Node {
	if n.X.Op() != ir.OLITERAL || n.X.Val().Kind() != constant.String {
		base.Fatalf("stringtoarraylit %v", n)
	}

	var list []ir.Node
	i := 0
	eltType := n.Type().Elem()
	for _, r := range ir.StringVal(n.X) {
		elt := ir.NewKeyExpr(base.Pos, ir.NewInt(int64(i)), ir.NewInt(int64(r)))
		// 从非类型化int更改为实际的元素类型
		// 由类型2确定。不需要改变英语教学。键，因为数组索引
		// 仅用于设置元素顺序。
		elt.Value.SetType(eltType)
		list = append(list, elt)
		i++
	}

	nn := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(n.Type()), nil)
	nn.List = list
	typed(n.Type(), nn)
	// 需要转换OCOMPLIT。
	return transformCompLit(nn)
}

// transformConv根据需要转换一个OCONV节点，基于所涉及的类型，
// etc.对应于类型检查。tcConv。
func transformConv(n *ir.ConvExpr) ir.Node {
	t := n.X.Type()
	op, why := typecheck.Convertop(n.X.Op() == ir.OLITERAL, t, n.Type())
	if op == ir.OXXX {
		// types2当前忽略pragmas，因此“notinheap”不匹配是
		// 一个与类型相关的错误，它没有捕捉到。Convertop将在此处捕获此错误
		// （请参阅
		// Convertop开头附近的两项检查），并在点头结束时报告。
		base.ErrorfAt(n.Pos(), "cannot convert %L to type %v%s", n.X, n.Type(), why)
		return n
	}
	n.SetOp(op)
	switch n.Op() {
	case ir.OCONVNOP:
		if t.Kind() == n.Type().Kind() {
			switch t.Kind() {
			case types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128:
				// 浮点型转换意味着舍入和
				// 因此必须保持转换。
				n.SetOp(ir.OCONV)
			}
		}

	// 不要转换为[]字节文字。见第125796条。
	// 如果没有它，生成的代码和编译器内存占用会更好。
	case ir.OSTR2BYTES:
		// 好的

	case ir.OSTR2RUNES:
		if n.X.Op() == ir.OLITERAL {
			return stringtoruneslit(n)
		}

	case ir.OBYTES2STR:
		assert(t.IsSlice())
		assert(t.Elem().Kind() == types.TUINT8)
		if t.Elem() != types.ByteType && t.Elem() != types.Types[types.TUINT8] {
			// 如果t是用户定义的字节类型B（不是uint8 
			// 或字节）的一个片段，那么从[]B向[]字节添加一个额外的CONVNOP，这样
			// 在walk中添加的对slicebytetostring（）的调用将正确进行
			// 类型检查。
			n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.ByteType), n.X)
			n.X.SetTypecheck(1)
		}

	case ir.ORUNES2STR:
		assert(t.IsSlice())
		assert(t.Elem().Kind() == types.TINT32)
		if t.Elem() != types.RuneType && t.Elem() != types.Types[types.TINT32] {
			// 如果t是用户定义的符文类型B（不是uint32 
			// 或符文）的一个片段，那么从[]B向[]符文添加一个额外的CONVNOP，这样
			// 在walk中添加的对slicerunetostring（）的调用将正确进行
			// 类型检查。
			n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.RuneType), n.X)
			n.X.SetTypecheck(1)
		}

	}
	return n
}

// transformConvCall转换转换调用。对应于
// typecheck的OTYPE部分。tcCall。
func transformConvCall(n *ir.CallExpr) ir.Node {
	assert(n.Type() != nil && n.Typecheck() == 1)
	arg := n.Args[0]
	n1 := ir.NewConvExpr(n.Pos(), ir.OCONV, nil, arg)
	typed(n.X.Type(), n1)
	return transformConv(n1)
}

// transformCall转换普通的函数/方法调用。对应于类型检查的最后一半
// （非转换，非内置部分）。tcCall。在OCALL/OFUNCINST的情况下，这段代码甚至可以工作。
func transformCall(n *ir.CallExpr) {
	// 设定基准。Pos，因为下面的transformArgs可能需要它，但是transformCall 
	// 在一些不设置基的过程中被调用。Pos.
	ir.SetPos(n)
	// n.Type（）对于没有返回值的调用可以为零
	assert(n.Typecheck() == 1)
	transformArgs(n)
	l := n.X
	t := l.Type()

	switch l.Op() {
	case ir.ODOTINTER:
		n.SetOp(ir.OCALLINTER)

	case ir.ODOTMETH:
		l := l.(*ir.SelectorExpr)
		n.SetOp(ir.OCALLMETH)

		tp := t.Recv().Type

		if l.X == nil || !types.Identical(l.X.Type(), tp) {
			base.Fatalf("method receiver")
		}

	default:
		n.SetOp(ir.OCALLFUNC)
	}

	typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args)
	if l.Op() == ir.ODOTMETH && len(deref(n.X.Type().Recv().Type).RParams()) == 0 {
		typecheck.FixMethodCall(n)
	}
	if t.NumResults() == 1 {
		if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME {
			if sym := n.X.(*ir.Name).Sym(); types.IsRuntimePkg(sym.Pkg) && sym.Name == "getg" {
				// Emit code for runtime。直接调用getg（）而不是调用函数。
				// 大多数这样的重写（例如math.Sqrt的类似重写）应该在walk中完成，
				// 这样排序过程就可以通过引入临时变量来确保保留原始代码
				// （尤其是函数调用的确切时间）。
				// 在这种情况下，我们知道getg（）在给定的函数中总是返回相同的结果
				// 并且我们希望避免临时变量，所以我们比通常情况下更早地进行重写。
				n.SetOp(ir.OGETG)
			}
		}
		return
	}
}

// transformEarlyCall将调用的参数转换为OfUncent节点。
func transformEarlyCall(n *ir.CallExpr) {
	transformArgs(n)
	typecheckaste(ir.OCALL, n.X, n.IsDDD, n.X.Type().Params(), n.Args)
}

// transformCompare转换比较操作（当前仅等于/不等于
// equals）。对应于
// 类型检查中的“比较运算符”情况。类型检查1，包括tcArith。
func transformCompare(n *ir.BinaryExpr) {
	assert(n.Type() != nil && n.Typecheck() == 1)
	if (n.Op() == ir.OEQ || n.Op() == ir.ONE) && !types.Identical(n.X.Type(), n.Y.Type()) {
		// 只要一方可分配给
		// 另一方，就可以进行比较。转换不为CONVNOP的唯一允许的情况是
		// /“concrete==interface”。在这种情况下，检查
		// 混凝土类型的可比性。转换是分配的，所以只有在
		// 混凝土类型很大。
		l, r := n.X, n.Y
		lt, rt := l.Type(), r.Type()
		converted := false
		if rt.Kind() != types.TBLANK {
			aop, _ := typecheck.Assignop(lt, rt)
			if aop != ir.OXXX {
				types.CalcSize(lt)
				if lt.HasShape() || rt.IsInterface() == lt.IsInterface() || lt.Size() >= 1<<16 {
					l = ir.NewConvExpr(base.Pos, aop, rt, l)
					l.SetTypecheck(1)
				}

				converted = true
			}
		}

		if !converted && lt.Kind() != types.TBLANK {
			aop, _ := typecheck.Assignop(rt, lt)
			if aop != ir.OXXX {
				types.CalcSize(rt)
				if rt.HasShape() || rt.IsInterface() == lt.IsInterface() || rt.Size() >= 1<<16 {
					r = ir.NewConvExpr(base.Pos, aop, lt, r)
					r.SetTypecheck(1)
				}
			}
		}
		n.X, n.Y = l, r
	}
}

// 对应于类型检查。这是它的星星。
func implicitstar(n ir.Node) ir.Node {
	// 如果固定数组需要，插入隐式*
	t := n.Type()
	if !t.IsPtr() {
		return n
	}
	t = t.Elem()
	if !t.IsArray() {
		return n
	}
	star := ir.NewStarExpr(base.Pos, n)
	star.SetImplicit(true)
	return typed(t, star)
}

// transformIndex转换索引操作。对应于打字检查。tcIndex。
func transformIndex(n *ir.IndexExpr) {
	assert(n.Type() != nil && n.Typecheck() == 1)
	n.X = implicitstar(n.X)
	l := n.X
	t := l.Type()
	if t.Kind() == types.TMAP {
		n.Index = assignconvfn(n.Index, t.Key())
		n.SetOp(ir.OINDEXMAP)
		// 将type设置为仅映射值，而不是（value，bool）。这是
		// 与types2不同，但更适合
		// 编译器的后期。
		n.SetType(t.Elem())
		n.Assigned = false
	}
}

// transformSlice转换切片操作。对应于打字检查。Tslice。
func transformSlice(n *ir.SliceExpr) {
	assert(n.Type() != nil && n.Typecheck() == 1)
	l := n.X
	if l.Type().IsArray() {
		addr := typecheck.NodAddr(n.X)
		addr.SetImplicit(true)
		typed(types.NewPtr(n.X.Type()), addr)
		n.X = addr
		l = addr
	}
	t := l.Type()
	if t.IsString() {
		n.SetOp(ir.OSLICESTR)
	} else if t.IsPtr() && t.Elem().IsArray() {
		if n.Op().IsSlice3() {
			n.SetOp(ir.OSLICE3ARR)
		} else {
			n.SetOp(ir.OSLICEARR)
		}
	}
}

// 语句的转换函数

// 对应于类型检查。检查分配。
func transformCheckAssign(stmt ir.Node, n ir.Node) {
	if n.Op() == ir.OINDEXMAP {
		n := n.(*ir.IndexExpr)
		n.Assigned = true
		return
	}
}

// 对应于类型检查。分配
func transformAssign(stmt ir.Node, lhs, rhs []ir.Node) {
	checkLHS := func(i int, typ *types.Type) {
		transformCheckAssign(stmt, lhs[i])
	}

	cr := len(rhs)
	if len(rhs) == 1 {
		if rtyp := rhs[0].Type(); rtyp != nil && rtyp.IsFuncArgStruct() {
			cr = rtyp.NumFields()
		}
	}

	// x，ok=y 
assignOK:
	for len(lhs) == 2 && cr == 1 {
		stmt := stmt.(*ir.AssignListStmt)
		r := rhs[0]

		switch r.Op() {
		case ir.OINDEXMAP:
			stmt.SetOp(ir.OAS2MAPR)
		case ir.ORECV:
			stmt.SetOp(ir.OAS2RECV)
		case ir.ODOTTYPE:
			r := r.(*ir.TypeAssertExpr)
			stmt.SetOp(ir.OAS2DOTTYPE)
			r.SetOp(ir.ODOTTYPE2)
		case ir.ODYNAMICDOTTYPE:
			r := r.(*ir.DynamicTypeAssertExpr)
			stmt.SetOp(ir.OAS2DOTTYPE)
			r.SetOp(ir.ODYNAMICDOTTYPE2)
		default:
			break assignOK
		}
		checkLHS(0, r.Type())
		checkLHS(1, types.UntypedBool)
		t := lhs[0].Type()
		if t != nil && rhs[0].Type().HasShape() && t.IsInterface() && !types.IdenticalStrict(t, rhs[0].Type()) {
			// 这是一个多值赋值（映射、通道或点类型）
			// 在
			// 赋值过程中，主要结果转换为一个接口。通常，所需的便利在（*orderState）之前不会创建
			// 。as2ok（），因为AS2*操作和它们的
			// 子操作是如此紧密地交织在一起。但是我们现在需要创建
			// Confiface来启用字典查找。因此，首先将
			// 结果分配给temp，这样我们就可以在将第一个temp分配给lhs[0]时体现
			// 的便利性。如果我们将
			// confivace直接添加到rhs[0]中，我们将破坏许多后来的
			// 代码，这些代码依赖于AS2*操作
			// 及其子操作之间的紧密耦合。（第50642期）。
			v := typecheck.Temp(rhs[0].Type())
			ok := typecheck.Temp(types.Types[types.TBOOL])
			as := ir.NewAssignListStmt(base.Pos, stmt.Op(), []ir.Node{v, ok}, []ir.Node{r})
			as.Def = true
			as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, v))
			as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, ok))
			as.SetTypecheck(1)
			// 将stmt更改为最终
			// 左侧的TEMP的正常分配。我们重新创建了原始的多值赋值
			// 以便将其赋值给temp，并将其添加为stmt的init。
			// 
			// TODO:修正求值顺序，使lhs[0]
			// 的lval在rhs[0]之前求值（类似于#50672中的问题）。
			stmt.SetOp(ir.OAS2)
			stmt.PtrInit().Append(as)
			// assignconvfn插入CONVIFACE。
			stmt.Rhs = []ir.Node{assignconvfn(v, t), ok}
		}
		return
	}

	if len(lhs) != cr {
		for i := range lhs {
			checkLHS(i, nil)
		}
		return
	}

	// x，y，z=f（）对应于类型检查。typecheckargs。实际上只是处理多值通话。
	if cr > len(rhs) {
		stmt := stmt.(*ir.AssignListStmt)
		stmt.SetOp(ir.OAS2FUNC)
		r := rhs[0].(*ir.CallExpr)
		rtyp := r.Type()

		mismatched := false
		failed := false
		for i := range lhs {
			result := rtyp.Field(i).Type
			checkLHS(i, result)

			if lhs[i].Type() == nil || result == nil {
				failed = true
			} else if lhs[i] != ir.BlankNode && !types.Identical(lhs[i].Type(), result) {
				mismatched = true
			}
		}
		if mismatched && !failed {
			typecheck.RewriteMultiValueCall(stmt, r)
		}
		return
	}

	for i, r := range rhs {
		checkLHS(i, r.Type())
		if lhs[i].Type() != nil {
			rhs[i] = assignconvfn(r, lhs[i].Type())
		}
	}
}

func transformArgs(n ir.InitNode) {
	var list []ir.Node
	switch n := n.(type) {
	default:
		base.Fatalf("transformArgs %+v", n.Op())
	case *ir.CallExpr:
		list = n.Args
		if n.IsDDD {
			return
		}
	case *ir.ReturnStmt:
		list = n.Results
	}
	if len(list) != 1 {
		return
	}

	t := list[0].Type()
	if t == nil || !t.IsFuncArgStruct() {
		return
	}

	// 将n另存为fmt的n.Orig。去
	if ir.Orig(n) == n {
		n.(ir.OrigNode).SetOrig(ir.SepCopy(n))
	}

	// 将f（g（））重写为t1，t2，…=g（）；f（t1，t2，…）。
	typecheck.RewriteMultiValueCall(n, list[0])
}

// assignconvfn将要赋值的节点n转换为类型t。对应于
// typecheck。这是fn。
func assignconvfn(n ir.Node, t *types.Type) ir.Node {
	if t.Kind() == types.TBLANK {
		return n
	}

	if n.Op() == ir.OPAREN {
		n = n.(*ir.ParenExpr).X
	}

	if types.IdenticalStrict(n.Type(), t) {
		return n
	}

	op, why := Assignop(n.Type(), t)
	if op == ir.OXXX {
		base.Fatalf("found illegal assignment %+v -> %+v; %s", n.Type(), t, why)
	}

	r := ir.NewConvExpr(base.Pos, op, t, n)
	r.SetTypecheck(1)
	r.SetImplicit(true)
	return r
}

func Assignop(src, dst *types.Type) (ir.Op, string) {
	if src == dst {
		return ir.OCONVNOP, ""
	}
	if src == nil || dst == nil || src.Kind() == types.TFORW || dst.Kind() == types.TFORW || src.Underlying() == nil || dst.Underlying() == nil {
		return ir.OXXX, ""
	}

	// 1。src type与dst（考虑形状）
	if types.Identical(src, dst) {
		// 我们已经从上面的assignconvfn中知道，IdenticalStrict（src，
		// dst）为false，因此类型不完全相同，
		// src或dst中的一个是形状。如果dst是一个接口（这意味着src也是
		// 一个接口），我们需要一个真正的OConvide op；否则我们需要
		// OCONVNOP。见第48453期。
		if dst.IsInterface() {
			return ir.OCONVIFACE, ""
		} else {
			return ir.OCONVNOP, ""
		}
	}
	return typecheck.Assignop1(src, dst)
}

// 对应于类型检查。typecheckast，但我们只添加了一个额外的标志
// only。如果ConfifaceOnly为真，我们只进行接口转换。我们用它来做
// 在noder2期间提前插入confiface节点，此时函数或参数可能
// 有typeparams。
func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl ir.Nodes) {
	var t *types.Type
	var i int

	lno := base.Pos
	defer func() { base.Pos = lno }()

	var n ir.Node
	if len(nl) == 1 {
		n = nl[0]
	}

	i = 0
	for _, tl := range tstruct.Fields().Slice() {
		t = tl.Type
		if tl.IsDDD() {
			if isddd {
				n = nl[i]
				ir.SetPos(n)
				if n.Type() != nil {
					nl[i] = assignconvfn(n, t)
				}
				return
			}

			// TODO（mdempsky）：变成。。。调用隐式切片。
			for ; i < len(nl); i++ {
				n = nl[i]
				ir.SetPos(n)
				if n.Type() != nil {
					nl[i] = assignconvfn(n, t.Elem())
				}
			}
			return
		}

		n = nl[i]
		ir.SetPos(n)
		if n.Type() != nil {
			nl[i] = assignconvfn(n, t)
		}
		i++
	}
}

// transformSend转换send语句，根据需要将值转换为相应的
// 通道类型。这是打字检查的结果。结束。
func transformSend(n *ir.SendStmt) {
	n.Value = assignconvfn(n.Value, n.Chan.Type().Elem())
}

// transformReturn通过执行所需的赋值和任何必要的转换来转换返回节点。对应于打字检查。tcReturn（）
func transformReturn(rs *ir.ReturnStmt) {
	transformArgs(rs)
	nl := rs.Results
	if ir.HasNamedResults(ir.CurFunc) && len(nl) == 0 {
		return
	}

	typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), nl)
}

// transformSelect转换一个select节点，根据需要为每种情况创建一个赋值列表
// 。对应于打字检查。tcSelect（）。
func transformSelect(sel *ir.SelectStmt) {
	for _, ncase := range sel.Cases {
		if ncase.Comm != nil {
			n := ncase.Comm
			oselrecv2 := func(dst, recv ir.Node, def bool) {
				selrecv := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv})
				if dst.Op() == ir.ONAME && dst.(*ir.Name).Defn == n {
					// 必须修复dst的Defn，因为我们正在彻底更改节点。
					dst.(*ir.Name).Defn = selrecv
				}
				selrecv.Def = def
				selrecv.SetTypecheck(1)
				selrecv.SetInit(n.Init())
				ncase.Comm = selrecv
			}
			switch n.Op() {
			case ir.OAS:
				// 将x=<-c转换为x，=<-c 
				// 删除隐式转换；最终的任务
				// 将重新引入它们。
				n := n.(*ir.AssignStmt)
				if r := n.Y; r.Op() == ir.OCONVNOP || r.Op() == ir.OCONVIFACE {
					r := r.(*ir.ConvExpr)
					if r.Implicit() {
						n.Y = r.X
					}
				}
				oselrecv2(n.X, n.Y, n.Def)

			case ir.OAS2RECV:
				n := n.(*ir.AssignListStmt)
				n.SetOp(ir.OSELRECV2)

			case ir.ORECV:
				// convert<-c to=<-c 
				n := n.(*ir.UnaryExpr)
				oselrecv2(ir.BlankNode, n, false)

			case ir.OSEND:
				break
			}
		}
	}
}

// transformAsOp转换赋值语句。对应于
// typecheck1中的OASOP大小写。
func transformAsOp(n *ir.AssignOpStmt) {
	transformCheckAssign(n, n.X)
}

// transformDot根据需要转换OXDOT（或ODOT）或ODOT、ODOTPTR、ODOTMETH、
// ODOTINTER或OMETHVALUE。它根据需要添加额外的节点来访问嵌入的字段。对应于打字检查。tcDot。
func transformDot(n *ir.SelectorExpr, isCall bool) ir.Node {
	assert(n.Type() != nil && n.Typecheck() == 1)
	if n.Op() == ir.OXDOT {
		n = typecheck.AddImplicitDots(n)
		n.SetOp(ir.ODOT)

		// 为任何新的ODOT节点设置选择字段和类型检查标志
		// 由AddImplicitTots（）添加，如果需要，还可以转换为ODOTPTR。相当于
		// tcDot中的“n.X=typecheck（n.X，ctxExpr | ctxType）”。
		for n1 := n; n1.X.Op() == ir.ODOT; {
			n1 = n1.X.(*ir.SelectorExpr)
			if !n1.Implicit() {
				break
			}
			t1 := n1.X.Type()
			if t1.IsPtr() && !t1.Elem().IsInterface() {
				t1 = t1.Elem()
				n1.SetOp(ir.ODOTPTR)
			}
			typecheck.Lookdot(n1, t1, 0)
			n1.SetTypecheck(1)
		}
	}

	t := n.X.Type()

	if n.X.Op() == ir.OTYPE {
		return transformMethodExpr(n)
	}

	if t.IsPtr() && !t.Elem().IsInterface() {
		t = t.Elem()
		n.SetOp(ir.ODOTPTR)
	}

	f := typecheck.Lookdot(n, t, 0)
	assert(f != nil)

	if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && !isCall {
		n.SetOp(ir.OMETHVALUE)
		// 将方法类型转换为函数类型。见第47775期。
		n.SetType(typecheck.NewMethodType(n.Type(), nil))
	}
	return n
}

// 对应于类型检查。typecheckMethodExpr。
func transformMethodExpr(n *ir.SelectorExpr) (res ir.Node) {
	t := n.X.Type()

	// 计算为t设置的方法。
	var ms *types.Fields
	if t.IsInterface() {
		ms = t.AllMethods()
	} else {
		mt := types.ReceiverBaseType(t)
		typecheck.CalcMethods(mt)
		ms = mt.AllMethods()

		// 当t 
		// 与m声明的接收器类型不同时，方法表达式t.m需要一个包装器。我们
		// 通常在编写
		// 运行时类型描述符时生成这些包装，这通常是针对在包范围内声明的
		// 类型执行的。但是，我们需要
		// 来确保为匿名
		// 接收器类型生成包装。
		if mt.Sym() == nil {
			typecheck.NeedRuntimeType(t)
		}
	}

	s := n.Sel
	m := typecheck.Lookdot1(n, s, t, ms, 0)
	if !t.HasShape() {
		// 如果t是由形状类型实例化的，那么找不到该方法是可以的，因为我们无论如何都会在泛型类型上使用这些方法。
		assert(m != nil)
	}

	n.SetOp(ir.OMETHEXPR)
	n.Selection = m
	n.SetType(typecheck.NewMethodType(m.Type, n.X.Type()))
	return n
}

// 对应于类型检查。出现了。
func transformAppend(n *ir.CallExpr) ir.Node {
	transformArgs(n)
	args := n.Args
	t := args[0].Type()
	assert(t.IsSlice())

	if n.IsDDD {
		if t.Elem().IsKind(types.TUINT8) && args[1].Type().IsString() {
			return n
		}

		args[1] = assignconvfn(args[1], t.Underlying())
		return n
	}

	as := args[1:]
	for i, n := range as {
		assert(n.Type() != nil)
		as[i] = assignconvfn(n, t.Elem())
	}
	return n
}

// 对应于类型检查。t复杂。
func transformComplex(n *ir.BinaryExpr) ir.Node {
	l := n.X
	r := n.Y

	assert(types.Identical(l.Type(), r.Type()))

	var t *types.Type
	switch l.Type().Kind() {
	case types.TFLOAT32:
		t = types.Types[types.TCOMPLEX64]
	case types.TFLOAT64:
		t = types.Types[types.TCOMPLEX128]
	default:
		panic(fmt.Sprintf("transformComplex: unexpected type %v", l.Type()))
	}

	// 必须在此处为泛型设置类型，因为这不能通过替换泛型类型来确定。
	typed(t, n)
	return n
}

// 对应于类型检查。删除。
func transformDelete(n *ir.CallExpr) ir.Node {
	transformArgs(n)
	args := n.Args
	assert(len(args) == 2)

	l := args[0]
	r := args[1]

	args[1] = assignconvfn(r, l.Type().Key())
	return n
}

// 对应于类型检查。tcMake。
func transformMake(n *ir.CallExpr) ir.Node {
	args := n.Args

	n.Args = nil
	l := args[0]
	t := l.Type()
	assert(t != nil)

	i := 1
	var nn ir.Node
	switch t.Kind() {
	case types.TSLICE:
		l = args[i]
		i++
		var r ir.Node
		if i < len(args) {
			r = args[i]
			i++
		}
		nn = ir.NewMakeExpr(n.Pos(), ir.OMAKESLICE, l, r)

	case types.TMAP:
		if i < len(args) {
			l = args[i]
			i++
		} else {
			l = ir.NewInt(0)
		}
		nn = ir.NewMakeExpr(n.Pos(), ir.OMAKEMAP, l, nil)
		nn.SetEsc(n.Esc())

	case types.TCHAN:
		l = nil
		if i < len(args) {
			l = args[i]
			i++
		} else {
			l = ir.NewInt(0)
		}
		nn = ir.NewMakeExpr(n.Pos(), ir.OMAKECHAN, l, nil)
	default:
		panic(fmt.Sprintf("transformMake: unexpected type %v", t))
	}

	assert(i == len(args))
	typed(n.Type(), nn)
	return nn
}

// 对应于类型检查。惊慌失措。
func transformPanic(n *ir.UnaryExpr) ir.Node {
	n.X = assignconvfn(n.X, types.Types[types.TINTER])
	return n
}

// 对应于类型检查。tcPrint。
func transformPrint(n *ir.CallExpr) ir.Node {
	transformArgs(n)
	return n
}

// 对应于类型检查。tcRealImag。
func transformRealImag(n *ir.UnaryExpr) ir.Node {
	l := n.X
	var t *types.Type

	// 确定结果类型。
	switch l.Type().Kind() {
	case types.TCOMPLEX64:
		t = types.Types[types.TFLOAT32]
	case types.TCOMPLEX128:
		t = types.Types[types.TFLOAT64]
	default:
		panic(fmt.Sprintf("transformRealImag: unexpected type %v", l.Type()))
	}

	// 必须在此处为泛型设置类型，因为这不能通过替换泛型类型来确定。
	typed(t, n)
	return n
}

// 对应于类型检查。Tlcancap。
func transformLenCap(n *ir.UnaryExpr) ir.Node {
	n.X = implicitstar(n.X)
	return n
}

// 对应于tcCall的内置部分。
func transformBuiltin(n *ir.CallExpr) ir.Node {
	// n.Type（）对于没有返回值的内置项可以为零
	assert(n.Typecheck() == 1)
	fun := n.X.(*ir.Name)
	op := fun.BuiltinOp

	switch op {
	case ir.OAPPEND, ir.ODELETE, ir.OMAKE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
		n.SetOp(op)
		n.X = nil
		switch op {
		case ir.OAPPEND:
			return transformAppend(n)
		case ir.ODELETE:
			return transformDelete(n)
		case ir.OMAKE:
			return transformMake(n)
		case ir.OPRINT, ir.OPRINTN:
			return transformPrint(n)
		case ir.ORECOVER:
			// 。Init 
			return n
		}

	case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL:
		transformArgs(n)
		fallthrough

	case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
		u := ir.NewUnaryExpr(n.Pos(), op, n.Args[0])
		switch op {
		case ir.OCAP, ir.OLEN:
			return transformLenCap(u1.(*ir.UnaryExpr))
		case ir.OREAL, ir.OIMAG:
			return transformRealImag(u1.(*ir.UnaryExpr))
		case ir.OPANIC:
			return transformPanic(u1.(*ir.UnaryExpr))
		case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
			// 这对应于typecheck（）末尾附近的EvalConst（）调用。
			return typecheck.EvalConst(u1)
		case ir.OCLOSE, ir.ONEW:
			// 无需更多操作
			return u1
		}

	case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE:
		transformArgs(n)
		b := ir.NewBinaryExpr(n.Pos(), op, n.Args[0], n.Args[1])
		n1 := typed(n.Type(), ir.InitExpr(n.Init(), b))
		if op != ir.OCOMPLEX {
			// 无需更多操作
			return n1
		}
		return transformComplex(n1.(*ir.BinaryExpr))

	default:
		panic(fmt.Sprintf("transformBuiltin: unexpected op %v", op))
	}

	return n
}

func hasKeys(l ir.Nodes) bool {
	for _, n := range l {
		if n.Op() == ir.OKEY || n.Op() == ir.OSTRUCTKEY {
			return true
		}
	}
	return false
}

// TransformArrayIt在每个数组元素上运行assignconvfn，并返回保存所有数组键/索引所需的片/数组长度
// （比最高索引多一个）。对应于打字检查。typecheckarraylit。
func transformArrayLit(elemType *types.Type, bound int64, elts []ir.Node) int64 {
	var key, length int64
	for i, elt := range elts {
		ir.SetPos(elt)
		r := elts[i]
		var kv *ir.KeyExpr
		if elt.Op() == ir.OKEY {
			elt := elt.(*ir.KeyExpr)
			key = typecheck.IndexConst(elt.Key)
			assert(key >= 0)
			kv = elt
			r = elt.Value
		}

		r = assignconvfn(r, elemType)
		if kv != nil {
			kv.Value = r
		} else {
			elts[i] = r
		}

		key++
		if key > length {
			length = key
		}
	}

	return length
}

// transformCompLit将n转换为OARRAYLIT、OSLICELIT、OMAPLIT或
// OSTRUCTLIT节点，并进行任何必要的转换。对应于
// 类型检查。tcCompLit（包括与tcStructLitKey对应的零件）。
func transformCompLit(n *ir.CompLitExpr) (res ir.Node) {
	assert(n.Type() != nil && n.Typecheck() == 1)
	lno := base.Pos
	defer func() {
		base.Pos = lno
	}()

	// 保存原始节点（包括n.右）
	n.SetOrig(ir.Copy(n))

	ir.SetPos(n)

	t := n.Type()

	switch t.Kind() {
	default:
		base.Fatalf("transformCompLit %v", t.Kind())

	case types.TARRAY:
		transformArrayLit(t.Elem(), t.NumElem(), n.List)
		n.SetOp(ir.OARRAYLIT)

	case types.TSLICE:
		length := transformArrayLit(t.Elem(), -1, n.List)
		n.SetOp(ir.OSLICELIT)
		n.Len = length

	case types.TMAP:
		for _, l := range n.List {
			ir.SetPos(l)
			assert(l.Op() == ir.OKEY)
			l := l.(*ir.KeyExpr)

			r := l.Key
			l.Key = assignconvfn(r, t.Key())

			r = l.Value
			l.Value = assignconvfn(r, t.Elem())
		}

		n.SetOp(ir.OMAPLIT)

	case types.TSTRUCT:
		// 需要有效的字段偏移量来进行下面的Xoffset。
		types.CalcSize(t)

		if len(n.List) != 0 && !hasKeys(n.List) {
			// 简单值列表
			ls := n.List
			for i, n1 := range ls {
				ir.SetPos(n1)

				f := t.Field(i)
				n1 = assignconvfn(n1, f.Type)
				ls[i] = ir.NewStructKeyExpr(base.Pos, f, n1)
			}
			assert(len(ls) >= t.NumFields())
		} else {
			// 键控列表
			ls := n.List
			for i, l := range ls {
				ir.SetPos(l)

				kv := l.(*ir.KeyExpr)
				key := kv.Key

				// 由于导入点，Sym可能已解析为其他顶级
				// 包中的名称。在执行查找之前，请重定向到正确的sym 
				// 。
				s := key.Sym()
				if id, ok := key.(*ir.Ident); ok && typecheck.DotImportRefs[id] != nil {
					s = typecheck.Lookup(s.Name)
				}
				if types.IsExported(s.Name) && s.Pkg != types.LocalPkg {
					// 导出的字段名应始终具有
					// 本地pkg。我们只需要对从另一个包导入的
					// 通用函数进行调整。
					// 转换后的
					s = typecheck.Lookup(s.Name)
				}

				// 一个OXDOT使用Sym字段来保存
				// 该字段位于该点的右侧，
				// 因此s将是非零的，但一个OXDOT 
				// 永远不是有效的结构文字键。
				assert(!(s == nil || key.Op() == ir.OXDOT || s.IsBlank()))

				f := typecheck.Lookdot1(nil, s, t, t.Fields(), 0)
				l := ir.NewStructKeyExpr(l.Pos(), f, kv.Value)
				ls[i] = l

				l.Value = assignconvfn(l.Value, f.Type)
			}
		}

		n.SetOp(ir.OSTRUCTLIT)
	}

	return n
}

// transformAddr对应于类型检查。tcAddr。
func transformAddr(n *ir.AddrExpr) {
	switch n.X.Op() {
	case ir.OARRAYLIT, ir.OMAPLIT, ir.OSLICELIT, ir.OSTRUCTLIT:
		n.SetOp(ir.OPTRLIT)
	}
}
